/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.marcelo.agro.core;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.prefs.Preferences;
import org.marcelo.parser.matematica.ParserString2Double;
import org.openide.util.NbPreferences;

/**
 *
 * @author marcelo
 * Usar patron singleton para poder inicializar las preferencias de la aplicacion
 * 
 * La idea de esta clase es la de proveer todos los formatos necesarios para la aplicacion
 * Crear el Formato para las fechas
 * Crear el formato para los numeros
 * String a Numero BigDecimal
 * BigDecimal a String
 * BigDecimal a Formato Moneda
 * Crear el Formato para las Monedas deacuerdo a la Ubicacion?
 * Crear formato en base a NBPreferences, y el simbolo asignado por el usuario, leer la info que el usuario seelccioo
 * Leer la posicion que el usuario desea que aparezca los datos
 */
public class UtilidadesFormatos {
    
    //Nombre de las variables
    private static String KEY_MONEDA = "simboloMoneda";
    private static String KEY_SEPARADOR_MILES = "simboloSeparadorMiles";
    private static String KEY_SEPARADOR_DECIMALES = "simboloSeparadorDecimales";
    private static String KEY_POSICION_SIMBOLO = "posicionSimbolo";
    private static String KEY_PRECISION = "precisionDecimal";
    private static String KEY_MODO_REDONDEO = "modoRedondeo";
    
    //Valores por defecto
    private static String DEFAULT_SIMBOLO_MONEDA = "USD";
    private static String DEFAULT_SEPARADOR_MILES = ",";
    private static String DEFAULT_SEPARADOR_DECIMALES = ".";
    private static String DEFAULT_PRECISION = "10";
    private static String DEFAULT_REDONDEO = "HALF_EVEN";
    //Solo dos valores posibles inicio y fin otros valores ocultaran
    private static String DEFAULT_POSICION_SIMBOLO = "fin";
    //Valores de las propiedades
    private static String VALOR_SIMBOLO_MONEDA;
    private static String VALOR_SEPARADOR_DECIMALES ;
    private static String VALOR_SEPARADOR_MILES ;
    private static String VALOR_POSICION_SIMBOLO ;
    private static String VALOR_PRECISION ;
    private static String VALOR_REDONDEO ;
    
    
    private Preferences nodo ;
    private static final Locale miUbicacion = Locale.getDefault();
    
    private static DateFormat formatoDias = new SimpleDateFormat("dd-MM-yyyy" , miUbicacion) ;
    private static DateFormat formatoMes = new SimpleDateFormat("MM-yyyy" , miUbicacion) ;
    private static DateFormat formatoAnho = new SimpleDateFormat("yyyy" , miUbicacion);
    private static DecimalFormat formatoNumero ;
    private static DecimalFormat formatoMoneda ;
    private static final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
    
    //Inicializacin de datos
    private static final Double NaN = Double.NaN;
    
    private UtilidadesFormatos() {
       //Obteniendo las preferencias
        nodo = NbPreferences.forModule(UtilidadesFormatos.class);
       VALOR_SIMBOLO_MONEDA = nodo.get(KEY_MONEDA, ""); 
       if (VALOR_SIMBOLO_MONEDA.equals("")) {
            //Significa que no se guardo la preferencia, entonces crearla
            nodo.put(KEY_MONEDA, DEFAULT_SIMBOLO_MONEDA);
            VALOR_SIMBOLO_MONEDA = DEFAULT_SIMBOLO_MONEDA;
        }
        VALOR_SEPARADOR_DECIMALES = nodo.get(KEY_SEPARADOR_DECIMALES,"");
        if( VALOR_SEPARADOR_DECIMALES.equals("")) {
            nodo.put(KEY_SEPARADOR_DECIMALES, DEFAULT_SEPARADOR_DECIMALES);
            VALOR_SEPARADOR_DECIMALES = DEFAULT_SEPARADOR_DECIMALES;
        }
        VALOR_SEPARADOR_MILES  = nodo.get(KEY_SEPARADOR_MILES,"");
        if (VALOR_SEPARADOR_MILES.equals("")){
            nodo.put(KEY_SEPARADOR_MILES, DEFAULT_SEPARADOR_MILES);
            VALOR_SEPARADOR_MILES = DEFAULT_SEPARADOR_MILES;
        }
        VALOR_POSICION_SIMBOLO = nodo.get(KEY_POSICION_SIMBOLO, "") ;
        if (VALOR_POSICION_SIMBOLO.equals("")) {
            nodo.put(KEY_POSICION_SIMBOLO, DEFAULT_POSICION_SIMBOLO);
            VALOR_POSICION_SIMBOLO = DEFAULT_POSICION_SIMBOLO;
        }
        //Construir los formatos
        formatoNumero = new DecimalFormat("#0"+VALOR_SEPARADOR_DECIMALES + "00#");
        formatoMoneda = new DecimalFormat("###"+VALOR_SEPARADOR_MILES+"###"+VALOR_SEPARADOR_MILES+"###"+VALOR_SEPARADOR_MILES+"##0"+VALOR_SEPARADOR_DECIMALES+"00#");

    }
    
   
    
    private NumberFormat getFormatoNumero(){
        return NumberFormat.getInstance(miUbicacion);
    }
    
    private NumberFormat getFormatoMoneda(){
        return NumberFormat.getCurrencyInstance(miUbicacion);
    }
    
    public DateFormat getFormatoFecha(){
        
        return formatoDias;
    }
    
    public DateFormat[] getFormatosFecha() {
        List<DateFormat> result = new ArrayList<DateFormat>();
        result.add(new SimpleDateFormat("yyyy/MM/dd" , miUbicacion) );
        result.add(new SimpleDateFormat("yyyy-MM-dd" , miUbicacion) );
        result.add(new SimpleDateFormat("dd-MM-yyyy" , miUbicacion) );
        result.add(new SimpleDateFormat("dd/MM/yyyy" , miUbicacion) );
        return result.toArray(new DateFormat[0]);
    }
    
    public DateFormat getFormatoFecha(String formato){
        if (formato != null)
            return new SimpleDateFormat(formato);
        else {
            return getFormatoFecha();
        }
    }
    
    public String getFormatoNumero(Object numeroAFormatear){
        return formatoNumero.format(numeroAFormatear);
//        return getFormatoNumero().format(numeroAFormater );
    }
    
    /**
     * Es metodo permite formatear un numero en formato moneda
     * su puede definir la posicion del simbolo de la moneda
     * @param numeroAFormatear
     * @return Un String con formato nomeda, con dos valores decimales y el simbolo de moneda
     */
    public String getFormatoMoneda(Object numeroAFormatear) {
        
        if (numeroAFormatear == null) {
            numeroAFormatear = new Double("0");
        }
        
        if (VALOR_POSICION_SIMBOLO.equals("inicio")) {
            return  VALOR_SIMBOLO_MONEDA + " " + formatoMoneda.format(numeroAFormatear);
        
        }
        if (VALOR_POSICION_SIMBOLO.equals("fin")) {
            return formatoMoneda.format(numeroAFormatear) + " " + getSimboloMoneda();
        }
        return formatoMoneda.format(numeroAFormatear);
    }
    
    
    public String getSimboloMoneda(){
        return VALOR_SIMBOLO_MONEDA;
    }
    
    private static UtilidadesFormatos format;

    
    
    /**
     * Obtener una instancia de la utilidad para formatear
     * el sistema
     * @return 
     */
    public static UtilidadesFormatos getInstance() {
        if (format == null) {
            format = new UtilidadesFormatos();
        }
        return format;
    }
    
    /**
     * Metodo para ser utilizado en todos los BigDecimal que realizan calculos
     * @return 
     */
    public MathContext getMathContext(){
        return mc;
    }
    
    /**
     * MEtodo para realizar divisiones con BigDecimal
     * Si uno de los operadores es nulo retornara BigDecimal.Zero
     */
    
    public BigDecimal divide(BigDecimal A, BigDecimal B) {
        
        
        if (A == null) {
            return BigDecimal.ZERO;
        }
        if (B == null) {
            return BigDecimal.ZERO;
        }
        
        if (B.compareTo(BigDecimal.ZERO) == 0) {
//            no se puede dividir
            printErrors("No se puede dividir por Cero");
            return BigDecimal.ZERO;
        }
        
        try {
            return A.divide(B);
        }
        catch (ArithmeticException ae) {
//            Se produjo un error de division entonces
//            setear el mathContext
            printErrors(ae);
            return A.divide(B, mc);
        
        }
        catch (NullPointerException npe) {
            printErrors(npe);
            return BigDecimal.ZERO;
        }
    }
    
    /**
     * Metodo para realizar multiplicaciones de manera uniforme en todo el sistema
     * 
     * @param A
     * @param B
     * @return 
     */
    public BigDecimal multiply(BigDecimal A, BigDecimal B){
        return A.multiply(B, mc);
    }
    
    private void printErrors(Object object){
            System.err.println("=========================INICIO Error al Dividir================");
            System.err.println(object);
            System.err.println("=========================FIN Error al Dividir================");
    }
    
    /**
     * Divide y formatea un numero
     */
    public String divideAndFormatNumero(BigDecimal A, BigDecimal B) {
        return  getFormatoNumero(divide(A, B)); 
    }
    
    /**
     * Metodo para parsear un String de entrada y realizar operaciones matematicas
     * luego retorna un Double
     */
    
    public Double str2Double(String aParser) {
        Double result;
        try{
            result = ParserString2Double.str2Double(aParser);
            if (result.isInfinite() || result.isNaN()) {
                return 0.00;
            }
            return result;
        }
        catch(Error e){
            GlobalContext.getInstance().Logger().println(e);
            return 0.00;
        }
        catch(Exception e) {
            GlobalContext.getInstance().Logger().println(e);
            return 0.00;
        }
    }
      
    public boolean isEmpty(String s) {
        return s == null || s.trim().isEmpty();
    }
}
